﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml.Linq;
using System.Threading;

namespace Klinkby.MicroBus
{
    /// <summary>
    /// Implements a basic <see cref="IAsyncFilter"/>
    /// </summary>
    [Serializable]
    internal abstract class AsyncFilter : XSerializable<IAsyncFilter>, IAsyncFilter
    {
        protected static IAsyncFilter[] NoChildren = new IAsyncFilter[0];
        readonly IAsyncFilter[] _children;

        protected AsyncFilter(IAsyncFilter[] children)
        {
            Debug.Assert(children != null);
            _children = children;
        }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
        }

        #endregion

        #region Dispose pattern

        protected virtual void Dispose(bool disposing)
        {
            GC.SuppressFinalize(this);
            if (disposing)
            {                
                Parallel.ForEach(_children, (child) => child.Dispose());
            }
        }

        ~AsyncFilter()
        {
            Dispose(false);
        }

        #endregion

        #region IAsyncFilter Members

        public virtual void Process(IMessage message)
        {
#if DEBUG
            Debug.WriteLine(GetType().Name + ".Process");
#endif
            int count = _children.Length;
            if (count > 0)
            {
                if (count == 1)
                    _children[0].Process(message);
                else // mange
                    Parallel.ForEach(_children, (child) => child.Process(message));
            }
        }

        public Action ProcessAsync(IMessage message, object state)
        {
#if DEBUG
            Debug.Assert(message != null);
            Debug.WriteLine(GetType().Name + ".ProcessAsync");
#endif
            bool cancelled = false;
            ThreadPool.QueueUserWorkItem(
            //Task task = Task.Create(
                (o1) =>
                {
                    Process(message);
                    OnProcessCompleted(new ProcessCompletedEventArgs(cancelled, /*Task.Current.Exception*/null, state));
                }, null);
            return () => { cancelled = true; /*task.Cancel();*/ };
        }

        protected virtual void OnProcessCompleted(ProcessCompletedEventArgs e)
        {
#if DEBUG
            Debug.WriteLine(GetType().Name + ".OnProcessCompleted");
#endif
            Safe.RaiseAsync(ProcessCompleted, this, e);
        }

        public event EventHandler<ProcessCompletedEventArgs> ProcessCompleted;

        #endregion
    }
}
